package com.feshfans.xmpp.client;

import com.feshfans.bridge.RoomBridge;
import com.feshfans.xmpp.XmppConnectionManager;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.muc.*;
import org.jivesoftware.smackx.muc.packet.MUCItem;
import org.jivesoftware.smackx.muc.packet.MUCUser;
import org.jivesoftware.smackx.pubsub.ItemsExtension;
import org.jivesoftware.smackx.xdata.Form;
import org.jxmpp.jid.*;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.jid.parts.Resourcepart;
import org.jxmpp.stringprep.XmppStringprepException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

public class RoomClient extends AbstractListernerClient {

    private static final Logger Log = LoggerFactory.getLogger(RoomClient.class);

    private static final RoomBridge roomBridge = RoomBridge.getInstance();
    private static final RoomClient ROOM_CLIENT = new RoomClient(XmppConnectionManager.acquire());

    private MultiUserChatManager multiUserChatManager;
    // 维护用户已加入的房间  <jid,MultiUserChat>，退群时应该注销到监听和从此 map 移除
    //private Map<String,MultiUserChat> multiUserChatMap = new HashMap<>();

    public static RoomClient getInstance(){
        return ROOM_CLIENT;
    }

    RoomClient(XMPPTCPConnection connection) {
        super(connection);
        multiUserChatManager = MultiUserChatManager.getInstanceFor(connection);
        initListener();
    }

    /**
     * 登录后，加载加入的所有房间
     */
    public List<MultiUserChat> loadRoom() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, XmppStringprepException, MultiUserChatException.NotAMucServiceException {

        List<MultiUserChat> allRoomList = new ArrayList<>();
        // 获取 server 端的 DomainJid
        List<DomainBareJid> domainBareJidList = multiUserChatManager.getXMPPServiceDomains();
        for(DomainBareJid domainBareJid : domainBareJidList){
            // 获取指定 DomainJid 的房间
            List<HostedRoom> roomList = multiUserChatManager.getHostedRooms(domainBareJid);
            if(roomList!=null){
                // 给房间添加监听
                for(HostedRoom room : roomList){
                    MultiUserChat multiUserChat = multiUserChatManager.getMultiUserChat(room.getJid());
                    addListenerForRoom(multiUserChat);
                    allRoomList.add(multiUserChat);
                }

            }
        }
        return allRoomList;
    }

    /**
     * 加载房间信息，在用户打开某个房间后
     * @param roomJid
     * @return
     */
    public MultiUserChat loadRoomInfo(EntityBareJid roomJid){
        return multiUserChatManager.getMultiUserChat(roomJid);
    }


    /**
     * 初始化监听
     */
    @Override
    protected void initListener(){
        // 添加加入房间的邀请
        multiUserChatManager.addInvitationListener(new MyInvitationListener());
    }


    /**
     * 创建房间(Reserved Room)，房间分两种: Instant Romm, Reserved Room
     * @param name 房间名称
     * @return boolean 是否创建成功，true:成功，false:失败
     */
    public boolean createReservedRoom(String name) throws XmppStringprepException, InterruptedException, SmackException.NoResponseException, MultiUserChatException.MucAlreadyJoinedException, SmackException.NotConnectedException, XMPPException.XMPPErrorException, MultiUserChatException.MissingMucCreationAcknowledgeException, MultiUserChatException.NotAMucServiceException {

        if(StringUtils.isEmpty(name)){
            Log.error("房间名称为空, name: {}", name);
            return false;
        }

        // 拼接房间的 jid, muc 根据不同服务器的不同而不同，tigase 为 muc, 而 openfire 为 conference
        String jid = name+"@muc."+connection.getXMPPServiceDomain();
        EntityBareJid entityBareJid = JidCreate.entityBareFrom(jid);

        MultiUserChat multiUserChat = multiUserChatManager.getMultiUserChat(entityBareJid);
        // 添加 room 监听
        addListenerForRoom(multiUserChat);
        // 获取当前用户名称
        String nickName = getCurrentUser().getLocalpartOrNull().toString();
        MultiUserChat.MucCreateConfigFormHandle formHandle = multiUserChat.create(Resourcepart.from(nickName));
        MucConfigFormManager configFormManager = formHandle.getConfigFormManager();

        // 获取房间的默认配置
        Form srcForm = multiUserChat.getConfigurationForm();
        // 根据源 form 创建应答 form
        Form destForm = srcForm.createAnswerForm();

        //destForm.setAnswer("muc#roomconfig_publicroom", true);
        // 设置聊天室是持久聊天室，即将要被保存下来
        //destForm.setAnswer("muc#roomconfig_persistentroom", true);
        // 房间仅对成员开放,非成员但看到但无法加入。如为 false, 人员可以发现此房间并自主选择加入，不需要任何验证过程
        destForm.setAnswer("muc#roomconfig_membersonly", true);
        // 允许占有者邀请其他人
        //destForm.setAnswer("muc#roomconfig_allowinvites", true);
        //进入不需要密码
        destForm.setAnswer("muc#roomconfig_passwordprotectedroom",  false);

        // 登录房间对话
        destForm.setAnswer("muc#roomconfig_enablelogging", true);
        // 仅允许注册的昵称登录
        //destForm.setAnswer("x-muc#roomconfig_reservednick", false);
        // 允许使用者修改昵称
        //destForm.setAnswer("x-muc#roomconfig_canchangenick", true);
        // 允许用户注册房间
        //destForm.setAnswer("x-muc#roomconfig_registration", false);
        // 发送已完成的表单（有默认值）到服务器来配置聊天室
        multiUserChat.sendConfigurationForm(destForm);

        return true;
    }


    /**
     * 邀请成员
     * @param multiUserChat
     * @param to
     * @throws XmppStringprepException
     * @throws SmackException.NotConnectedException
     * @throws InterruptedException
     */
    public void invite(MultiUserChat multiUserChat, String to) throws XmppStringprepException, SmackException.NotConnectedException, InterruptedException {

        String toJid = to+"@"+connection.getXMPPServiceDomain();
        EntityBareJid entityBareJid = JidCreate.entityBareFrom(toJid);

        multiUserChat.invite(entityBareJid,"");
    }


    public void tichu(MultiUserChat multiUserChat, String to){
    }


    /**
     * 有以下3种情况应调用此方法：
     * 1.当前用户创建新房间
     * 2.登录时加载已有的房间
     * 3.被邀请加入某个房间
     * @param multiUserChat
     */
    private void addListenerForRoom(MultiUserChat multiUserChat){
        // 邀请被拒绝监听
        multiUserChat.addInvitationRejectionListener(new MyInvitationRejectionListener());
        // 房间消息监听
        multiUserChat.addMessageListener(new MyMessageListener());
        // 出度监听
        multiUserChat.addParticipantListener(new MyPresenceListener());
        // 房间人员状态变化监听
        multiUserChat.addParticipantStatusListener(new MyParticipantStatusListener());
        // 房间主题变化监听
        multiUserChat.addSubjectUpdatedListener(new MySubjectUpdatedListener());
        // 房间中好友状态变化监听
        multiUserChat.addUserStatusListener(new MyUserStatusListener());

    }

    /**
     * 邀请加入房间监听
     */
    static class MyInvitationListener implements InvitationListener{

        /**
         * 处理收到的邀请请求
         *
         * @param conn            与 server 建立的长链接
         * @param room            房间对象
         * @param inviter         邀请者
         * @param reason          邀请原因
         * @param password        进入房间的密码，如果有的话
         * @param message         消息
         * @param invitation
         */
        @Override
        public void invitationReceived(XMPPConnection conn, MultiUserChat room, EntityJid inviter, String reason, String password, Message message, MUCUser.Invite invitation) {
            Log.info("invitationReceived:{},{},{},{},{},{}",room.getSubject(),inviter.toString(),reason,password,message.toXML(),invitation.toXML());
        }
    }

    /**
     * 房间发出邀请被拒绝监听，当前用户发出的邀请被拒绝时触发
     */
    static class MyInvitationRejectionListener implements InvitationRejectionListener{

        /**
         * 源解释可见上层说明
         * @param invitee       被邀请人，即拒绝邀请的人
         * @param reason        拒绝邀请原因
         * @param message       用于拒绝邀请的 message
         * @param rejection
         */
        @Override
        public void invitationDeclined(EntityBareJid invitee, String reason, Message message, MUCUser.Decline rejection) {
            Log.info("invitationDeclined:{},{},{},{}",invitee.toString(),reason,message.toXML(),rejection.toXML());
        }
    }

    /**
     * 房间的消息监听
     */
    static class MyMessageListener implements MessageListener{

        @Override
        public void processMessage(Message message) {
            Log.info("processMessage:{}",message.toXML());

            roomBridge.processMessage(message);
        }
    }

    /**
     * 房间出席监听
     */
    static class MyPresenceListener implements PresenceListener{

        @Override
        public void processPresence(Presence presence) {
            Jid jid = presence.getFrom();

            MUCUser mucUser = (MUCUser) presence.getExtension("x",
                    "http://jabber.org/protocol/muc#user");
            MUCItem item = mucUser.getItem();
            if(item == null){
                return;
            }
            Log.info("jid:{},nick:{},affiliation:{}",item.getJid(),item.getNick(),item.getAffiliation());

            roomBridge.processPresence(jid, item.getJid(),item.getNick(), item.getAffiliation());
        }
    }


    /**
     * muc room 的 subject 发生改变时被触发
     */
    static class MySubjectUpdatedListener implements SubjectUpdatedListener{

        @Override
        public void subjectUpdated(String subject, EntityFullJid from) {
            Log.info("subjectUpdated:{},{}",subject,from.toString());
        }
    }

    static class MyUserStatusListener implements UserStatusListener{

        @Override
        public void kicked(Jid actor, String reason) {

        }

        @Override
        public void voiceGranted() {

        }

        @Override
        public void voiceRevoked() {

        }

        @Override
        public void banned(Jid actor, String reason) {

        }

        @Override
        public void membershipGranted() {

        }

        @Override
        public void membershipRevoked() {

        }

        @Override
        public void moderatorGranted() {

        }

        @Override
        public void moderatorRevoked() {

        }

        @Override
        public void ownershipGranted() {

        }

        @Override
        public void ownershipRevoked() {

        }

        @Override
        public void adminGranted() {

        }

        @Override
        public void adminRevoked() {

        }

        @Override
        public void roomDestroyed(MultiUserChat alternateMUC, String reason) {

        }
    }


    /**
     * 成员状态发生变化时被触发，比如，管理员权限被撤销
     */
    static class MyParticipantStatusListener implements ParticipantStatusListener{

        @Override
        public void joined(EntityFullJid participant) {
            Log.info("joined:{}",participant.toString());
        }

        @Override
        public void left(EntityFullJid participant) {
            Log.info("left:{}",participant.toString());
        }

        @Override
        public void kicked(EntityFullJid participant, Jid actor, String reason) {
            Log.info("kicked:{}",participant.toString());
        }

        @Override
        public void voiceGranted(EntityFullJid participant) {
            Log.info("voiceGranted:{}",participant.toString());
        }

        @Override
        public void voiceRevoked(EntityFullJid participant) {
            Log.info("voiceRevoked:{}",participant.toString());
        }

        @Override
        public void banned(EntityFullJid participant, Jid actor, String reason) {
            Log.info("banned:{}",participant.toString());
        }

        @Override
        public void membershipGranted(EntityFullJid participant) {
            Log.info("membershipGranted:{}",participant.toString());
        }

        @Override
        public void membershipRevoked(EntityFullJid participant) {
            Log.info("membershipRevoked:{}",participant.toString());
        }

        @Override
        public void moderatorGranted(EntityFullJid participant) {
            Log.info("moderatorGranted:{}",participant.toString());
        }

        @Override
        public void moderatorRevoked(EntityFullJid participant) {
            Log.info("moderatorRevoked:{}",participant.toString());
        }

        @Override
        public void ownershipGranted(EntityFullJid participant) {
            Log.info("ownershipGranted:{}",participant.toString());
        }

        @Override
        public void ownershipRevoked(EntityFullJid participant) {
            Log.info("ownershipRevoked:{}",participant.toString());
        }

        /**
         * 用户被赋予管理员权限
         * @param participant
         */
        @Override
        public void adminGranted(EntityFullJid participant) {
            Log.info("adminGranted:{}",participant.toString());
        }

        /**
         * 用户被撤销管理员权限
         * @param participant
         */
        @Override
        public void adminRevoked(EntityFullJid participant) {
            Log.info("adminRevoked:{}",participant.toString());
        }

        @Override
        public void nicknameChanged(EntityFullJid participant, Resourcepart newNickname) {
            Log.info("nicknameChanged:{},{}",participant.toString(),newNickname.toString());
        }
    }


}
